using System;
using System.Collections.Generic;
using System.IO;
using System.Xml;
using System.Linq;
using System.Xml.Linq;

// See the ReadMe.html for additional information
class XQueryUseCases
{
    //<bib>
    // {
    //  for $b in doc("http://bstore1.example.com/bib.xml")/bib/book
    //  where $b/publisher = "Addison-Wesley" and $b/@year > 1991
    //  return
    //    <book year="{ $b/@year }">
    //     { $b/title }
    //    </book>
    // }
    //</bib> 

    public static XElement Q1() {
        XDocument bib = XDocument.Load(dataPath + "bib.xml");
        return new XElement("bib",
            from b in bib.Element("bib").Elements("book")
            where (string)b.Element("publisher") == "Addison-Wesley" && (int)b.Attribute("year") > 1991
            select new XElement("book",
                    b.Attribute("year"),
                    b.Elements("title")
                    )
            );
    }

    //<results>
    //  {
    //    for $b in doc("http://bstore1.example.com/bib.xml")/bib/book,
    //        $t in $b/title,
    //        $a in $b/author
    //    return
    //        <result>
    //            { $t }    
    //            { $a }
    //        </result>
    //  }
    //</results>

    public static XElement Q2() {
        XDocument bib = XDocument.Load(dataPath + "bib.xml");
        return new XElement("results",
            from b in bib.Element("bib").Elements("book")
            from t in b.Elements("title")
            from a in b.Elements("author")
            select new XElement("result", t, a)
            );
    }

    //<results>
    //{
    //    for $b in doc("http://bstore1.example.com/bib.xml")/bib/book
    //    return
    //        <result>
    //            { $b/title }
    //            { $b/author  }
    //        </result>
    //}
    //</results> 

    public static XElement Q3() {
        XDocument bib = XDocument.Load(dataPath + "bib.xml");
        return new XElement("results",
            from b in bib.Element("bib").Elements("book")
            select new XElement("result",
                    b.Elements("title"),
                    b.Elements("author")
                    )
            );
    }

    //<results>
    //  {
    //    let $a := doc("http://bstore1.example.com/bib/bib.xml")//author
    //    for $last in distinct-values($a/last),
    //        $first in distinct-values($a[last=$last]/first)
    //    order by $last, $first
    //    return
    //        <result>
    //            <author>
    //               <last>{ $last }</last>
    //               <first>{ $first }</first>
    //            </author>
    //            {
    //                for $b in doc("http://bstore1.example.com/bib.xml")/bib/book
    //                where some $ba in $b/author 
    //                      satisfies ($ba/last = $last and $ba/first=$first)
    //                return $b/title
    //            }
    //        </result>
    //  }
    //</results> 

    public static XElement Q4() {
        XDocument bib = XDocument.Load(dataPath + "bib.xml");
        return new XElement("results",
            from b in bib.Element("bib").Elements("book")
            from a in b.Elements("author")
            group b by a into g
            orderby g.Key
            select 
                new XElement("result",
                    g.Key,
                    from b in g select b.Element("title")
                    )
            );
    }

    //public static XElement Q4a() {
    //    XElement bib = XElement.Load(dataPath + "bib.xml");
    //    return new XElement("results",
    //        bib.Elements("book").SelectMany(b =>
    //            b.Elements("author").Select(a =>
    //                new {
    //                    Title = b.Element("title"),
    //                    Author = new {
    //                        Last = (string)a.Element("last"),
    //                        First = (string)a.Element("first")
    //                    }
    //                }
    //            )
    //        ).
    //        GroupBy(x => x.Author).
    //        OrderBy(g => g.Key.Last).
    //        Select(g =>
    //            new XElement("result",
    //                new XElement("author",
    //                    new XElement("last", g.Key.Last),
    //                    new XElement("first", g.Key.First)
    //                ),
    //                g.Group.Select(x => x.Title)
    //            )
    //        )
    //    );
    //}

    //<books-with-prices>
    //  {
    //    for $b in doc("http://bstore1.example.com/bib.xml")//book,
    //        $a in doc("http://bstore2.example.com/reviews.xml")//entry
    //    where $b/title = $a/title
    //    return
    //        <book-with-prices>
    //            { $b/title }
    //            <price-bstore2>{ $a/price/text() }</price-bstore2>
    //            <price-bstore1>{ $b/price/text() }</price-bstore1>
    //        </book-with-prices>
    //  }
    //</books-with-prices>

    public static XElement Q5() {
        XDocument bib = XDocument.Load(dataPath + "bib.xml");
        XDocument reviews = XDocument.Load(dataPath + "reviews.xml");
        return new XElement("books-with-prices",
            from b in bib.Descendants("book")
            from a in reviews.Descendants("entry")
            where (string)b.Element("title") == (string)a.Element("title")
            select new XElement("book-with-prices",
                    b.Element("title"),
                    new XElement("price-bstore2", (string)a.Element("price")),
                    new XElement("price-bstore1", (string)b.Element("price"))
                    )
            );
    }

    //<bib>
    //  {
    //    for $b in doc("http://bstore1.example.com/bib.xml")//book
    //    where count($b/author) > 0
    //    return
    //        <book>
    //            { $b/title }
    //            {
    //                for $a in $b/author[position()<=2]  
    //                return $a
    //            }
    //            {
    //                if (count($b/author) > 2)
    //                 then <et-al/>
    //                 else ()
    //            }
    //        </book>
    //  }
    //</bib>

    public static XElement Q6() {
        XDocument bib = XDocument.Load(dataPath + "bib.xml");
        return new XElement("bib",
            from b in bib.Descendants("book")
            where b.Elements("author").Any()
            select new XElement("book",
                    b.Element("title"),
                    b.Elements("author").Take(2),
                    b.Elements("author").Count() > 2 ? new XElement("et-al") : null
                    )
            );
    }

    //<bib>
    //  {
    //    for $b in doc("http://bstore1.example.com/bib.xml")//book
    //    where $b/publisher = "Addison-Wesley" and $b/@year > 1991
    //    order by $b/title
    //    return
    //        <book>
    //            { $b/@year }
    //            { $b/title }
    //        </book>
    //  }
    //</bib> 

    public static XElement Q7() {
        XDocument bib = XDocument.Load(dataPath + "bib.xml");
        return new XElement("bib",
            from b in bib.Descendants("book")
            where (string)b.Element("publisher") == "Addison-Wesley" && (int)b.Attribute("year") > 1991
            orderby (string)b.Element("title")
            select new XElement("book",
                    b.Attribute("year"),
                    b.Element("title")
                    )
            );
    }

    //for $b in doc("http://bstore1.example.com/bib.xml")//book
    //let $e := $b/*[contains(string(.), "Suciu") 
    //               and ends-with(local-name(.), "or")]
    //where exists($e)
    //return
    //    <book>
    //        { $b/title }
    //        { $e }
    //    </book> 

    public static XElement Q8() {
        XDocument bib = XDocument.Load(dataPath + "bib.xml");
        return new XElement("results",
            from b in bib.Descendants("book")
            let e = from x in b.Elements()
                    where x.Value.Contains("Suciu") && 
                          x.Name.LocalName.EndsWith("or")
                    select x
            where e.Any()
            select new XElement("book", b.Element("title"), e)
            );
    }

    //<results>
    //  {
    //    for $t in doc("books.xml")//(chapter | section)/title
    //    where contains($t/text(), "XML")
    //    return $t
    //  }
    //</results> 

    public static XElement Q9() {
        XDocument book = XDocument.Load(dataPath + "books.xml");
        XName chapter = "chapter";
        XName section = "section";
        return new XElement("results",
            book.Descendants().
            Where(e => e.Name == chapter || e.Name == section).
            Elements("title").
            Where(t => t.Value.Contains("XML"))
        );
    }

    //<results>
    //  {
    //    let $doc := doc("prices.xml")
    //    for $t in distinct-values($doc//book/title)
    //    let $p := $doc//book[title = $t]/price
    //    return
    //      <minprice title="{ $t }">
    //        <price>{ min($p) }</price>
    //      </minprice>
    //  }
    //</results> 

    public static XElement Q10() {
        XDocument prices = XDocument.Load(dataPath + "prices.xml");
        return new XElement("results",
            from b in prices.Descendants("book")
            group b by (string)b.Element("title") into g
            select 
                new XElement("minprice",
                    new XAttribute("title", g.Key),
                    new XElement("price", g.Min(b => (double)b.Element("price")))
                )
            );
    }

    //<bib>
    //{
    //        for $b in doc("http://bstore1.example.com/bib.xml")//book[author]
    //        return
    //            <book>
    //                { $b/title }
    //                { $b/author }
    //            </book>
    //}
    //{
    //        for $b in doc("http://bstore1.example.com/bib.xml")//book[editor]
    //        return
    //          <reference>
    //            { $b/title }
    //            {$b/editor/affiliation}
    //          </reference>
    //}
    //</bib>  

    public static XElement Q11() {
        XDocument bib = XDocument.Load(dataPath + "bib.xml");
        return new XElement("bib",
            from b in bib.Descendants("book")
            where b.Elements("author").Any()
            select new XElement("book",
                    b.Elements("title"),
                    b.Elements("author")
                    ),
            from b in bib.Descendants("book")
            where b.Elements("editor").Any()
            select new XElement("reference",
                    b.Elements("title"),
                    b.Elements("editor").Elements("affiliation")
                    )
            );
    }

    //<bib>
    //{
    //    for $book1 in doc("http://bstore1.example.com/bib.xml")//book,
    //        $book2 in doc("http://bstore1.example.com/bib.xml")//book
    //    let $aut1 := for $a in $book1/author 
    //                 order by $a/last, $a/first
    //                 return $a
    //    let $aut2 := for $a in $book2/author 
    //                 order by $a/last, $a/first
    //                 return $a
    //    where $book1 << $book2
    //    and not($book1/title = $book2/title)
    //    and deep-equal($aut1, $aut2) 
    //    return
    //        <book-pair>
    //            { $book1/title }
    //            { $book2/title }
    //        </book-pair>
    //}
    //</bib> 

    public static XElement Q12() {
        XDocument bib = XDocument.Load(dataPath + "bib.xml");
        var books = bib.Descendants("book").Select((book, index) =>
            new {
                book,
                index,
                authors = book.Elements("author").OrderBy(a => a.Value)
            }
        );
        return new XElement("bib",
            from b1 in books
            from b2 in books
            where b1.index < b2.index &&
                  b1.book.Element("title").Value != b2.book.Element("title").Value &&
                  b1.authors.SequenceEqual(b2.authors)
            select
                  new XElement("book-pair",
                    b1.book.Element("title"),
                    b2.book.Element("title")
                    )
            );
    }

    //declare function local:toc($book-or-section as element()) as element()*
    //{
    //    for $section in $book-or-section/section
    //    return
    //      <section>
    //         { $section/@* , $section/title , local:toc($section) }                 
    //      </section>
    //};

    //<toc>
    //   {
    //     for $s in doc("book.xml")/book return local:toc($s)
    //   }
    //</toc> 

    public static XElement QB1() {
        XDocument book = XDocument.Load(dataPath + "book.xml");
        return new XElement("toc", TableOfContents(book.Element("book")));
    }

    public static IEnumerable<XElement> TableOfContents(XElement bookOrSection) {
        return bookOrSection.Elements("section").Select(s =>
            new XElement("section", s.Attributes(), s.Element("title"), TableOfContents(s))
        );
    }

    //<figlist>
    //  {
    //    for $f in doc("book.xml")//figure
    //    return
    //        <figure>
    //            { $f/@* }
    //            { $f/title }
    //        </figure>
    //  }
    //</figlist> 

    public static XElement QB2() {
        XDocument book = XDocument.Load(dataPath + "book.xml");
        return new XElement("figlist",
            from f in book.Descendants("figure")
            select
                new XElement("figure",
                    f.Attributes(),
                    f.Elements("title")
                )
            );
    }

    //<section_count>{ count(doc("book.xml")//section) }</section_count>, 
    //<figure_count>{ count(doc("book.xml")//figure) }</figure_count> 

    public static XElement QB3() {
        XDocument book = XDocument.Load(dataPath + "book.xml");
        return new XElement("results",
            new XElement("section_count", book.Descendants("section").Count()),
            new XElement("figure_count", book.Descendants("figure").Count())
        );
    }

    //<top_section_count>
    // { 
    //   count(doc("book.xml")/book/section) 
    // }
    //</top_section_count>

    public static XElement QB4() {
        XDocument book = XDocument.Load(dataPath + "book.xml");
        return new XElement("top_section_count", book.Element("book").Elements("section").Count());
    }

    public static string dataPath;
}

class Program
{
    static void Main(string [] args) {
        SetDataPath();
        Console.WriteLine(XQueryUseCases.Q1());
		Console.ReadLine();
    }

    static void SetDataPath() {
        string path = Environment.CommandLine;
        while (path.StartsWith("\"")) {
            path = path.Substring(1, path.Length - 2);
        }
        while (path.EndsWith("\"") || path.EndsWith(" ")) {
            path = path.Substring(0, path.Length - 2);
        }
        path = Path.GetDirectoryName(path);

        XQueryUseCases.dataPath = Path.Combine(path, "data\\");
    }
}

